package com.tianji.promotion.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tianji.promotion.constants.PromotionConstants;
import com.tianji.promotion.domain.po.Coupon;
import com.tianji.promotion.domain.po.ExchangeCode;
import com.tianji.promotion.mapper.ExchangeCodeMapper;
import com.tianji.promotion.service.IExchangeCodeService;
import com.tianji.promotion.utils.CodeUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

/**
 * <p>
 * 兑换码 服务实现类
 * </p>
 *
 * @author zt
 * @since 2024-09-20
 */
@Service
@RequiredArgsConstructor
public class ExchangeCodeServiceImpl extends ServiceImpl<ExchangeCodeMapper, ExchangeCode> implements IExchangeCodeService {
    private final StringRedisTemplate redisTemplate;
    /***
     * 异步生成兑换码
     * @param coupon
     */
    @Async("generateExchangeCodeExecutor")//使用generateExchangeCodeExecutor 自定义线程池中的线程异步执行
    @Override
    public void asyncGenerateExchangeCode(Coupon coupon) {
        Integer totalNum = coupon.getTotalNum();//代表优惠券发放的总数量，也代表需要生成的兑换码的总数量
        //方式1：循环兑换码总数量 循环中单个获取自增id incr (效率不高)
        //方式2：先调用incrby 得到自增id最大值 然后再循环生成兑换码(只需要操作一次redis，所以效率更高)
        //1.生成自增id 借助redis的incrby方法
        Long increment = redisTemplate.opsForValue().increment(PromotionConstants.COUPON_CODE_SERIAL_KEY, totalNum);
        if(increment==null){
            return;
        }
        int maxSerialNum = increment.intValue();//本地自增长id的最大值
        int begin = maxSerialNum - totalNum;//自增id的循环开始值
        //2.循环生成兑换码 调用工具类生成兑换码
        List<ExchangeCode> list = new ArrayList<>();
        for(int serialNum = begin; serialNum <= maxSerialNum; serialNum++){
            //参数1为自增id的值，参数2为优惠券id(内部会计算出0~15之间的值，用于生成新鲜值)
            String code = CodeUtil.generateCode(serialNum, coupon.getId());
            ExchangeCode exchangeCode = new ExchangeCode();
            exchangeCode.setId(serialNum);//兑换码id ExchangeCode这个po类的主键生成策略要设置为INPUT
            exchangeCode.setCode(code);
            exchangeCode.setExchangeTargetId(coupon.getId());//优惠券id
            exchangeCode.setExpiredTime(coupon.getIssueEndTime());//兑换码  兑换的截止时间就是优惠券领取的截止时间
            list.add(exchangeCode);
        }
        //3.将兑换码保存db  exchange_code 批量保存
        this.saveBatch(list);
        // 4.写入Redis缓存，member：couponId，score：兑换码的最大序列号
        redisTemplate.opsForZSet().add(PromotionConstants.COUPON_RANGE_KEY, coupon.getId().toString(), maxSerialNum);
    }

    @Override
    public boolean updateExchangeCodeMark(long serialNum, boolean flag) {
        String key = PromotionConstants.COUPON_CODE_SERIAL_KEY;
        //修改兑换码的自增id对应的offset的值
        Boolean aBoolean = redisTemplate.opsForValue().setBit(key, serialNum, flag);//return的是redis之前保存的值
        return aBoolean!=null && aBoolean;
    }
}
